package com.gzstv.dlnasdk.upnp;

import com.gzstv.dlnasdk.util.HttpUtil;
import com.gzstv.dlnasdk.util.XmlParser;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SSDPClient {

    private static final String SSDP_IP = "239.255.255.250";
    private static final int SSDP_PORT = 1900;
    private static final String SSDP_MSEARCH = "M-SEARCH * HTTP/1.1\r\n"
            + "HOST: " + SSDP_IP + ":" + SSDP_PORT + "\r\n"
            + "MAN: \"ssdp:discover\"\r\n"
            + "MX: 3\r\n"
            + "ST: urn:schemas-upnp-org:device:MediaRenderer:1\r\n\r\n";

    private MulticastSocket multicastSocket;
    private ExecutorService executorService;
    private SSDPResponseListener responseListener;

    public interface SSDPResponseListener {
        void onDeviceDiscovered(Device device);
    }

    public SSDPClient(SSDPResponseListener listener) {
        this.responseListener = listener;
        // Initialize executorService once and reuse it
        executorService = Executors.newSingleThreadExecutor();
    }

    public void startDiscovery() {
        // If executorService is terminated, create a new one (should not happen with current fix)
        if (executorService.isShutdown() || executorService.isTerminated()) {
            executorService = Executors.newSingleThreadExecutor();
        }

        executorService.submit(() -> {
            try {
                multicastSocket = new MulticastSocket(SSDP_PORT);
                multicastSocket.setReuseAddress(true);
                InetAddress group = InetAddress.getByName(SSDP_IP);
                multicastSocket.joinGroup(group);

                DatagramPacket datagramPacket = new DatagramPacket(SSDP_MSEARCH.getBytes(), SSDP_MSEARCH.length(), group, SSDP_PORT);
                multicastSocket.send(datagramPacket);

                byte[] buf = new byte[1024];
                while (!Thread.currentThread().isInterrupted()) {
                    DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
                    try {
                        multicastSocket.receive(receivePacket);
                        String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
                        parseSSDPResponse(response);
                    } catch (SocketException e) {
                        // Socket closed, gracefully exit
                        if (multicastSocket.isClosed()) {
                            break;
                        }
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // Do not close executorService here, it should be managed by DLNAManager
                if (multicastSocket != null) {
                    multicastSocket.close();
                }
            }
        });
    }

    public void stopDiscovery() {
        // Only close the socket, do not shut down the executorService
        if (multicastSocket != null) {
            multicastSocket.close();
        }
    }

    private void parseSSDPResponse(String response) {
        String location = null;
        String udn = null;

        for (String line : response.split("\n")) {
            if (line.startsWith("LOCATION:")) {
                location = line.substring("LOCATION:".length()).trim();
            } else if (line.startsWith("USN:")) {
                udn = line.substring("USN:".length()).trim();
            }
        }

        if (location != null && udn != null) {
            try {
                // Fetch device description XML
                String xmlContent = HttpUtil.get(location);
                Device device = new Device("", udn, location); // Friendly name will be updated from XML
                XmlParser.parseDeviceDescription(device, xmlContent);

                if (responseListener != null) {
                    responseListener.onDeviceDiscovered(device);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}